﻿using System;
using System.Collections.Generic;
using MapDownloader.Core.Models;

namespace MapDownloader.Core.MapProviders
{
   public class BaiduMapProvider : BaseMapProvider
   {
      public static readonly BaiduMapProvider Instance;

      //以下是根据百度地图JavaScript API破解得到 百度坐标<->墨卡托坐标 转换算法
      private static readonly double[] Array1 = {75, 60, 45, 30, 15, 0};
      private static readonly double[] Array3 = {12890594.86, 8362377.87, 5591021, 3481989.83, 1678043.12, 0};

      private static readonly double[][] Array2 =
      {
         new[]
         {
            -0.0015702102444, 111320.7020616939, 1704480524535203, -10338987376042340, 26112667856603880,
            -35149669176653700, 26595700718403920, -10725012454188240, 1800819912950474, 82.5
         },
         new[]
         {
            0.0008277824516172526, 111320.7020463578, 647795574.6671607, -4082003173.641316, 10774905663.51142,
            -15171875531.51559, 12053065338.62167, -5124939663.577472, 913311935.9512032, 67.5
         },
         new[]
         {
            0.00337398766765, 111320.7020202162, 4481351.045890365, -23393751.19931662, 79682215.47186455,
            -115964993.2797253, 97236711.15602145, -43661946.33752821, 8477230.501135234, 52.5
         },
         new[]
         {
            0.00220636496208, 111320.7020209128, 51751.86112841131, 3796837.749470245, 992013.7397791013,
            -1221952.21711287, 1340652.697009075, -620943.6990984312, 144416.9293806241, 37.5
         },
         new[]
         {
            -0.0003441963504368392, 111320.7020576856, 278.2353980772752, 2485758.690035394, 6070.750963243378,
            54821.18345352118, 9540.606633304236, -2710.55326746645, 1405.483844121726, 22.5
         },
         new[]
         {
            -0.0003218135878613132, 111320.7020701615, 0.00369383431289, 823725.6402795718, 0.46104986909093,
            2351.343141331292, 1.58060784298199, 8.77738589078284, 0.37238884252424, 7.45
         }
      };

      private static double[][] array4 =
      {
         new[]
         {
            1.410526172116255e-8, 0.00000898305509648872, -1.9939833816331, 200.9824383106796, -187.2403703815547,
            91.6087516669843, -23.38765649603339, 2.57121317296198, -0.03801003308653, 17337981.2
         },
         new[]
         {
            -7.435856389565537e-9, 0.000008983055097726239, -0.78625201886289, 96.32687599759846, -1.85204757529826,
            -59.36935905485877, 47.40033549296737, -16.50741931063887, 2.28786674699375, 10260144.86
         },
         new[]
         {
            -3.030883460898826e-8, 0.00000898305509983578, 0.30071316287616, 59.74293618442277, 7.357984074871,
            -25.38371002664745, 13.45380521110908, -3.29883767235584, 0.32710905363475, 6856817.37
         },
         new[]
         {
            -1.981981304930552e-8, 0.000008983055099779535, 0.03278182852591, 40.31678527705744, 0.65659298677277,
            -4.44255534477492, 0.85341911805263, 0.12923347998204, -0.04625736007561, 4482777.06
         },
         new[]
         {
            3.09191371068437e-9, 0.000008983055096812155, 0.00006995724062, 23.10934304144901, -0.00023663490511,
            -0.6321817810242, -0.00663494467273, 0.03430082397953, -0.00466043876332, 2555164.4
         },
         new[]
         {
            2.890871144776878e-9, 0.000008983055095805407, -3.068298e-8, 7.47137025468032, -0.00000353937994,
            -0.02145144861037, -0.00001234426596, 0.00010322952773, -0.00000323890364, 826088.5
         }
      };

      private static PixelPoint LatLng2Mercator(LatLngPoint p)
      {
         double[] arr = null;
         var nLat = p.Lat > 74 ? 74 : p.Lat;
         nLat = nLat < -74 ? -74 : nLat;

         for (var i = 0; i < Array1.Length; i++)
            if (p.Lat >= Array1[i])
            {
               arr = Array2[i];
               break;
            }

         if (arr == null)
            for (var i = Array1.Length - 1; i >= 0; i--)
               if (p.Lat <= -Array1[i])
               {
                  arr = Array2[i];
                  break;
               }

         var res = Convertor(p.Lng, p.Lat, arr);
         return new PixelPoint((float) res[0], (float) res[1]);
      }

      private static double[] Convertor(double x, double y, double[] param)
      {
         var T = param[0] + param[1] * Math.Abs(x);
         var cC = Math.Abs(y) / param[9];
         var cF = param[2] + param[3] * cC + param[4] * cC * cC + param[5] * cC * cC * cC +
                  param[6] * cC * cC * cC * cC + param[7] * cC * cC * cC * cC * cC +
                  param[8] * cC * cC * cC * cC * cC * cC;
         T *= x < 0 ? -1 : 1;
         cF *= y < 0 ? -1 : 1;
         return new[] {T, cF};
      }

      /// <summary>
      ///    经纬度转像素坐标
      /// </summary>
      /// <param name="p"></param>
      /// <param name="zoom"></param>
      /// <returns></returns>
      public static PixelPoint GetLocationByLatLng(LatLngPoint p, int zoom)
      {
         var mercatorPoint = LatLng2Mercator(p); //墨卡托坐标
         return new PixelPoint((float) (mercatorPoint.X / Math.Pow(2, 18 - zoom)), (float) (mercatorPoint.Y / Math.Pow(2, 18 - zoom)));
      }

      static BaiduMapProvider()
      {
         Instance = new BaiduMapProvider {MaxZoom = 18};
      }

      protected override string Name => "Baidu";

      public override BaseImage GetTileImage(TilePoint pos)
      {
         return GetTileUsingHttp(MakeTileImageUrl(pos));
      }

      protected override List<List<LatLngPoint>> GetBoundary(string area)
      {
         var boundary =
            "119.665325, 34.260666;119.640496, 34.252038;119.582466, 34.215251;119.567621, 34.21095;119.557861, 34.213555;119.537799, 34.194656;119.509509, 34.17861;119.515733, 34.166648;119.534427, 34.173186;119.538525, 34.151763;119.532643, 34.150912;119.534187, 34.144088;119.526505, 34.143099;119.542704, 34.11525;119.556587, 34.121647;119.5641, 34.107672;119.575035, 34.111966;119.593717, 34.097043;119.591026, 34.084454;119.584675, 34.07713;119.570459, 34.075703;119.559107, 34.066426;119.575028, 34.038513;119.572334, 34.037508;119.577244, 34.026733;119.56948, 34.016026;119.56049, 34.024594;119.546345, 34.022795;119.541247, 34.03699;119.527802, 34.039375;119.523109, 34.037714;119.524578, 34.031857;119.517533, 34.028754;119.5144, 34.023498;119.503038, 34.020739;119.444495, 33.985495;119.443563, 33.990386;119.435271, 33.991688;119.430672, 34.009683;119.414439, 34.008295;119.410205, 34.010791;119.410589, 34.026846;119.404266, 34.030951;119.397102, 34.043485;119.386929, 34.043126;119.386855, 34.038841;119.38247, 34.037653;119.339535, 34.036174;119.330989, 34.031246;119.325291, 34.031976;119.324572, 34.038751;119.308627, 34.040727;119.305123, 34.047622;119.272712, 34.048776;119.273369, 34.0406;119.241727, 34.041895;119.24101, 34.049764;119.232445, 34.058571;119.214791, 34.061966;119.19603, 34.082404;119.189272, 34.095944;119.180329, 34.099409;119.17153, 34.110956;119.181431, 34.124507;119.173552, 34.136032;119.158786, 34.141368;119.155484, 34.14687;119.148038, 34.140285;119.135454, 34.150877;119.151054, 34.164565;119.14701, 34.167775;119.15065, 34.180419;119.142198, 34.183973;119.13819, 34.211495;119.104995, 34.213715;119.102381, 34.238622;119.096444, 34.242617;119.097185, 34.268062;119.078284, 34.272655;119.088023, 34.316673;119.078323, 34.337433;119.080728, 34.348646;119.067446, 34.34486;119.06218, 34.362755;119.061388, 34.388579;119.068934, 34.405134;119.067082, 34.424556;119.046534, 34.428264;119.043738, 34.409904;119.04019, 34.409586;119.03835, 34.405233;119.027598, 34.407293;119.028672, 34.41413;119.000286, 34.409856;118.997344, 34.398312;119.000636, 34.395358;118.977672, 34.373132;118.96065, 34.366386;118.958953, 34.373807;118.947683, 34.385457;118.936754, 34.377105;118.914972, 34.373901;118.870446, 34.371247;118.841249, 34.374499;118.82728, 34.365615;118.817878, 34.365014;118.814623, 34.34578;118.790489, 34.318067;118.776376, 34.314588;118.763539, 34.318738;118.765895, 34.326132;118.7536, 34.336507;118.745218, 34.330748;118.73793, 34.330662;118.736059, 34.335159;118.723403, 34.337748;118.696838, 34.352479;118.68154, 34.351734;118.682668, 34.346355;118.67795, 34.346521;118.649915, 34.364233;118.650336, 34.383549;118.668126, 34.390143;118.679481, 34.403447;118.674016, 34.41339;118.676262, 34.425353;118.661978, 34.426773;118.660514, 34.432639;118.654036, 34.43277;118.651449, 34.445748;118.639483, 34.445774;118.613224, 34.452692;118.603085, 34.451357;118.595694, 34.442027;118.590791, 34.425607;118.583295, 34.42272;118.565729, 34.434814;118.537944, 34.441427;118.529504, 34.446105;118.509067, 34.436395;118.495098, 34.426098;118.494981, 34.421909;118.471893, 34.412938;118.457948, 34.418206;118.450511, 34.416797;118.439801, 34.419622;118.429574, 34.428777;118.420689, 34.426082;118.418598, 34.437045;118.413641, 34.441855;118.421268, 34.477998;118.435607, 34.491591;118.442337, 34.507831;118.444787, 34.532297;118.428861, 34.585292;118.437984, 34.626114;118.444633, 34.632938;118.454622, 34.625677;118.464091, 34.624692;118.470354, 34.640644;118.465057, 34.670051;118.471008, 34.680822;118.489003, 34.676557;118.501651, 34.677224;118.511562, 34.691124;118.52811, 34.697865;118.530327, 34.718378;118.546414, 34.717268;118.554428, 34.711831;118.578229, 34.719155;118.60586, 34.720629;118.610712, 34.701033;118.638615, 34.692603;118.664255, 34.702072;118.688016, 34.684391;118.695775, 34.684735;118.716907, 34.697794;118.746821, 34.699858;118.759733, 34.708217;118.772409, 34.711287;118.775656, 34.718697;118.789297, 34.728557;118.784407, 34.734429;118.769168, 34.746264;118.746175, 34.742286;118.733822, 34.752195;118.724119, 34.751411;118.721097, 34.754621;118.721717, 34.76955;118.733376, 34.774498;118.74387, 34.772172;118.746685, 34.784655;118.734781, 34.793908;118.745397, 34.797915;118.761766, 34.795095;118.779523, 34.801922;118.784291, 34.814055;118.781888, 34.823893;118.787657, 34.832805;118.772951, 34.844739;118.772872, 34.850278;118.793914, 34.851969;118.802438, 34.849244;118.807377, 34.851642;118.812299, 34.884074;118.836515, 34.919172;118.865373, 34.947483;118.864671, 34.967849;118.87145, 35.004393;118.867742, 35.023743;118.870218, 35.03486;118.891123, 35.039816;118.915895, 35.053562;118.932219, 35.056905;118.942915, 35.048817;118.950832, 35.046915;118.976712, 35.053035;118.998245, 35.053674;119.009293, 35.059742;119.067011, 35.057282;119.080312, 35.062515;119.120654, 35.060578;119.125691, 35.063699;119.125847, 35.076187;119.134818, 35.081909;119.145414, 35.103562;119.163259, 35.106939;119.177917, 35.113467;119.200938, 35.116405;119.226068, 35.11246;119.232936, 35.119721;119.240847, 35.121757;119.245915, 35.128741;119.291249, 35.12147;119.297001, 35.117699;119.299769, 35.107483;119.306985, 35.104013;119.303301, 35.102202;119.305734, 35.095433;119.302921, 35.09031;119.359888, 35.037624;119.352806, 35.029374;119.348416, 34.907882;119.37289, 34.876762;119.44907, 34.84904;119.502807, 34.81978;119.582486, 34.790214;119.59381, 34.776863;119.607666, 34.740809;119.617958, 34.67011;119.62904, 34.648159;119.692899, 34.614214;119.756657, 34.584828;119.804728, 34.568171;119.862047, 34.555897;119.796352, 34.476081;119.803999, 34.439396;119.792604, 34.412961;119.806087, 34.394274;119.807587, 34.385387;119.78628, 34.375538;119.779542, 34.362031;119.759559, 34.353505;119.76068, 34.33451;119.75014, 34.328047;119.745336, 34.315229;119.731566, 34.308343;119.714533, 34.29217;119.665325, 34.260666";
         var result = new List<List<LatLngPoint>>();
         var pointList = new List<LatLngPoint>();
         var boundaryPoints = boundary.Split(";");
         foreach (var point in boundaryPoints)
         {
            var pointArr = point.Split(",");
            pointList.Add(new LatLngPoint(double.Parse(pointArr[1]), double.Parse(pointArr[0])));
         }

         result.Add(pointList);

         return result;
      }

      private string MakeTileImageUrl(TilePoint pos)
      {
         var url = string.Format(UrlFormat, pos.X, pos.Y, pos.Z);
         Console.BackgroundColor = ConsoleColor.Blue;
         Console.WriteLine(url);
         Console.ResetColor();
         return url;
      }

      public override List<StorageQueueItem> GetTilePoint(LatLngPoint minPointLatLng, LatLngPoint maxPointLatLng)
      {
         var zoomStart = MinZoom;
         var zoomEnd = MaxZoom;
         var result = new List<StorageQueueItem>();
         for (var zoom = zoomStart; zoom <= zoomEnd; ++zoom)
         {
            var point1 = GetLocationByLatLng(minPointLatLng, zoom); //将第一个点经纬度转换成平面2D坐标
            var point2 = GetLocationByLatLng(maxPointLatLng, zoom); //将第二个点经纬度转换成平面2D坐标

            var startX = (int) point1.X / 256; //起始列
            var endX = (int) point2.X / 256; //结束列
            if (endX == Math.Pow(2, zoom)) //结束列超出范围
               endX--;
            var startY = (int) point1.Y / 256; //起始行
            var endY = (int) point2.Y / 256; //结束行
            if (endY == Math.Pow(2, zoom)) //结束行超出范围
               endY--;


            for (var y = startY; y <= endY; y++)
            for (var x = startX; x <= endX; x++)
               result.Add(new StorageQueueItem {X = x, Y = y, Zoom = zoom});
         }

         Console.WriteLine($"共有{result.Count}张瓦片需要下载");
         return result;
      }
      
      //static readonly string UrlFormat = "http://wprd03.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={0}&y={1}&z={2}&scl=1&ltype=19";
      private static readonly string UrlFormat =
         "http://online3.map.bdimg.com/onlinelabel/?qt=tile&x={0}&y={1}&z={2}&styles=pl&udt=20170712&scaler=1&p=1";
   }
}